#pragma warning(disable: 4005)
//
#include "NeroCore\NMemoryManager.h"
//
#include "DirectX\Device.h"
//
#include "Win32Input.h"
#include "Win32Platform.h"

using namespace System;

Win32Input::Win32Input(void)
	:mKeyboard(NULL),
	 mMouse(NULL)
{
}

Win32Input::~Win32Input(void)
{
	Deinitialize();
}

bool Win32Input::Initialize()
{
	HRESULT result;
	if(GetPlatform()->GetDevice())
	{
		Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();

		// Initialize the main direct input interface.
		result = DirectInput8Create(nDevice->GetWindowsSettings().Instance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&mDirectInput, NULL);
		CHECK(result);

		mKeyboard = MY_NEW(Memory::HID_Engine,"Keyboard") Keyboard();
		mKeyboard->Initialize(mDirectInput, nDevice->GetWindowsSettings().WindowHandle);

		mMouse = MY_NEW(Memory::HID_Engine,"Mouse") Mouse();
		mMouse->Initialize(mDirectInput, nDevice->GetWindowsSettings().WindowHandle);

		mGamePads = MY_NEW(Memory::HID_Engine,"GamePads") GamePad[4];

		return true;
	}
	return false;
}

void Win32Input::Deinitialize()
{
	if(mKeyboard)
	{
		MY_DELETE(mKeyboard);
		mKeyboard = NULL;
	}

	if(mMouse)
	{
		MY_DELETE(mMouse);
		mMouse = NULL;
	}

	if(mGamePads)
	{
		MY_DELETE_ARRAY(mGamePads);
		mGamePads = NULL;
	}
}

void Win32Input::Update(float gameTime)
{
	if(mKeyboard)
	{
		mKeyboard->Update(gameTime);
	}

	if(mMouse)
	{
		mMouse->Update(gameTime);
	}
}

void Win32Input::HandleKeyDown(unsigned wParam, long)
{
	mKeyboard->mKeys[(unsigned int)wParam] = true;
}

void Win32Input::HandleKeyUp(unsigned wParam, long)
{
	mKeyboard->mKeys[(unsigned int)wParam] = false;
}

bool Win32Input::IsKeyDown(int keyValue)
{
	if(mKeyboard)
	{
		return mKeyboard->mKeys[keyValue];
	}
	return false;
}

Mouse* Win32Input::GetMouse()
{
	return mMouse;
}

#pragma region GAMEPAD
GamePad::GamePad()
{
}

GamePad::~GamePad()
{
}

void GamePad::Update(float)
{
	mPreviousControllerState = mCurrentControllerState;
	GetState();
}

XINPUT_STATE GamePad::GetState()
{
    // Zeroise the state
    ZeroMemory(&mCurrentControllerState, sizeof(XINPUT_STATE));

    // Get the state
    XInputGetState(mControllerNumber, &mCurrentControllerState);

    return mCurrentControllerState;
}

bool GamePad::IsConnected()
{
    // Zeroise the state
    ZeroMemory(&mCurrentControllerState, sizeof(XINPUT_STATE));

    // Get the state
    DWORD Result = XInputGetState(mControllerNumber, &mCurrentControllerState);

    if(Result == ERROR_SUCCESS)
    {
        return true;
    }
	return false;
}

void GamePad::Vibrate(int leftVal, int rightVal)
{
    // Create a Vibraton State
    XINPUT_VIBRATION Vibration;

    // Zeroise the Vibration
    ZeroMemory(&Vibration, sizeof(XINPUT_VIBRATION));

    // Set the Vibration Values
    Vibration.wLeftMotorSpeed = (WORD)leftVal;
    Vibration.wRightMotorSpeed = (WORD)rightVal;

    // Vibrate the controller
    XInputSetState(mControllerNumber, &Vibration);
}

#pragma endregion

#pragma region KEYBOARD
Keyboard::Keyboard()
	:mKeyboardDevice(NULL)
{
}

Keyboard::~Keyboard()
{
}

bool Keyboard::Initialize(IDirectInput8* device, HWND)
{
	// Initialize the direct input interface for the keyboard.
	HRESULT result = device->CreateDevice(GUID_SysKeyboard, &mKeyboardDevice, NULL);
	CHECK(result);

	// Set the data format.  In this case since it is a keyboard we can use the predefined data format.
	result = mKeyboardDevice->SetDataFormat(&c_dfDIKeyboard);
	WIN32_CHECK(result);

	// Set the cooperative level of the keyboard to not share with other programs.
	/*result = mKeyboardDevice->SetCooperativeLevel(winHandle, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
	WIN32_CHECK(result);*/

	// Initialize all the keys to being released and not pressed.
	for(int i=0; i<256; i++)
	{
		mKeys[i] = false;
	}

	// Acquire the keyboard.
	mKeyboardDevice->Acquire();
	return true;
}

void Keyboard::Update(float)
{
	// Acquire the keyboard.
	//mKeyboardDevice->Acquire();
	//memcpy(mLastState, mCurrentState,KEY_COUNT);
	//mKeyboardDevice->GetDeviceState(KEY_COUNT, (LPVOID) mCurrentState);
}

IDirectInputDevice8* Keyboard::GetDevice()
{
	return mKeyboardDevice;
}

#pragma endregion

#pragma region MOUSE
Mouse::Mouse()
	:mMouseDevice(NULL)
{
}

Mouse::~Mouse()
{
}

void Mouse::Initialize(IDirectInput8* device,  HWND winHandle)
{
	// Initialize the direct input interface for the mouse.
	HRESULT result = device->CreateDevice(GUID_SysMouse, &mMouseDevice, NULL);
	WIN32_CHECK(result);

	// Set the data format for the mouse using the pre-defined mouse data format.
	result = mMouseDevice->SetDataFormat(&c_dfDIMouse);
	WIN32_CHECK(result);

	// Set the cooperative level of the mouse to share with other programs.
	result = mMouseDevice->SetCooperativeLevel(winHandle, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	WIN32_CHECK(result);

	// Acquire the mouse.
	result = mMouseDevice->Acquire();
	WIN32_CHECK(result);

	GetCursorPos(&mPosition);
	ScreenToClient(winHandle, &mPosition);

	mDeltaX = 0;
	mDeltaY = 0;

	Update(0.0f);
}

void Mouse::Update(float)
{
	mPrevStateInfo.lX = mDeltaX;
	mPrevStateInfo.lY = mDeltaY;
	mPrevStateInfo.lZ = mCurrentStateInfo.lZ;
	
	memcpy(mPrevStateInfo.rgbButtons, mCurrentStateInfo.rgbButtons, 4);

	HRESULT result = mMouseDevice->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&mCurrentStateInfo);
	WIN32_CHECK(result);

	mPosition.x += mCurrentStateInfo.lX;
	mPosition.y += mCurrentStateInfo.lY;

	mDeltaX += mCurrentStateInfo.lX;
	mDeltaY += mCurrentStateInfo.lY;
}

IDirectInputDevice8* Mouse::GetDevice()
{
	return mMouseDevice;
}

long Mouse::DeltaX() const
{
	return mPrevStateInfo.lX - mDeltaX;
}
long Mouse::DeltaY() const
{
	return mPrevStateInfo.lY - mDeltaY;
}

#pragma endregion